package cn.yzj.openapi.service.impl;

import cn.yzj.common.CommonSymbol;
import cn.yzj.openapi.basic.AliCloudOssBase;
import cn.yzj.openapi.common.Common;
import cn.yzj.openapi.dao.intf.ApiSettingsDao;
import cn.yzj.openapi.entity.ApiSettingsEntity;
import cn.yzj.openapi.enums.error.RandomImgErrorEnums;
import cn.yzj.openapi.exception.OpenApiException;
import cn.yzj.openapi.service.RandomImgService;
import cn.yzj.openapi.utils.SettingsJsonUtils;
import cn.yzj.openapi.utils.StrUtils;
import cn.yzj.utils.AliUtils;
import cn.yzj.utils.ThreadPoolConfigUtils;
import com.google.common.cache.Cache;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

/**
 * 随机图服务实现类
 *
 * @author gzkemays
 * @since 2022/3/24 19:29
 */
@Service
public class RandomImgServiceImpl implements RandomImgService {

  @Resource(name = "settingCache")
  Cache<String, Object> settingCache;

  @Resource AliCloudOssBase ossBase;
  @Resource ApiSettingsDao settingsDao;
  static final List<String> TYPE_MISMATCH = new ArrayList<>();

  static {
    TYPE_MISMATCH.add("image/gif");
    TYPE_MISMATCH.add("image/jpeg");
    TYPE_MISMATCH.add("image/bmp");
    TYPE_MISMATCH.add("image/tiff");
    TYPE_MISMATCH.add("image/png");
  }

  @Override
  public String getRandomImg() {
    Object imgSet = settingCache.getIfPresent(ApiSettingsDao.IMG_COUNT_LIST_CACHE_KEY);
    if (imgSet == null) {
      settingsDao.resetSettingCache(ApiSettingsDao.IMG);
    }
    List<Integer> list =
        new ArrayList<>(
            (Set<Integer>)
                Objects.requireNonNull(
                    settingCache.getIfPresent(ApiSettingsDao.IMG_COUNT_LIST_CACHE_KEY)));
    int random = list.size() == 1 ? 0 : new Random().nextInt(list.size());
    Integer integer = list.get(random);
    list.remove(random);
    // 小于等于 1 时重置
    if (list.size() <= 1) {
      settingsDao.resetSettingCache(ApiSettingsDao.IMG);
      return OSS_IMG_API.replace(IMG_KEY, integer + ".jpg");
    } else {
      settingCache.put(ApiSettingsDao.IMG_COUNT_LIST_CACHE_KEY, new HashSet<>(list));
    }
    return OSS_IMG_API.replace(IMG_KEY, integer + ".jpg");
  }

  @Override
  @Transactional(rollbackFor = {Exception.class})
  public String upload(MultipartFile[] files) {
    check(files);
    ApiSettingsEntity apiSettingsEntity = settingsDao.selectById(Common.BEST_AUTH_POWER);
    int picMaxCount =
        Integer.parseInt(
            SettingsJsonUtils.getSettingsVal(
                apiSettingsEntity.getSettings(), ApiSettingsDao.PIC_MAX_COUNT));
    int count;
    if (files.length > OPEN_THREAD_COUNT) {
      count = threadBatchUpload(files, picMaxCount);
    } else {
      count = singleBatchUpload(files, picMaxCount);
    }
    if (count != 0 && count != picMaxCount) {
      apiSettingsEntity.setSettings(
          SettingsJsonUtils.builder()
              .setSettingsVal(ApiSettingsDao.PIC_MAX_COUNT, count)
              .buildSettingsVal());
      settingsDao.update(apiSettingsEntity);
      return "OK";
    }
    return "FAIL";
  }

  private void check(MultipartFile[] files) {
    if (files == null || files.length == 0) {
      throw OpenApiException.randomImgException(RandomImgErrorEnums.UPLOAD_IMG_IS_NULL);
    }
    for (MultipartFile file : files) {
      if (!TYPE_MISMATCH.contains(file.getContentType())) {
        throw OpenApiException.randomImgException(RandomImgErrorEnums.UPLOAD_IMG_ONLY_PIC);
      }
    }
  }

  private int singleBatchUpload(MultipartFile[] files, int count) {
    for (MultipartFile file : files) {
      String ext =
          StrUtils.getLastSplitHasSplitAfterStr(file.getOriginalFilename(), CommonSymbol.FULL_STOP);
      try {
        count++;
        AliUtils.uploadImgToOSS(
            ossBase.buildConfig(String.valueOf(count), ext, file.getInputStream()));
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
    return count;
  }

  private int threadBatchUpload(MultipartFile[] files, int count) {
    List<Future<Integer>> futures = new ArrayList<>();
    ThreadPoolConfigUtils.builder()
        .createThreadPool()
        .corePoolSize(3)
        .maximumPoolSize(6)
        .operation(
            (e) -> {
              Future<Integer> submit = e.submit(() -> singleBatchUpload(files, count));
              futures.add(submit);
            });
    if (!futures.isEmpty()) {
      List<Integer> counts = new ArrayList<>();
      for (Future<Integer> future : futures) {
        try {
          counts.add(future.get());
        } catch (InterruptedException | ExecutionException e) {
          e.printStackTrace();
        }
      }
      Collections.sort(counts);
      return counts.get(counts.size() - 1);
    }
    return 0;
  }
}
